package cn.com.chengmandian.agent.filters;

import cn.com.chengmandian.agent.properties.AuthProperties;
import cn.com.chengmandian.agent.utils.AuthUtil;
import cn.com.chengmandian.core.common.constants.CommonConstant;
import cn.com.chengmandian.core.common.constants.ResponseCode;
import cn.com.chengmandian.core.common.enums.ClientsEnum;
import cn.com.chengmandian.core.redis.cache.AgentCacheService;
import cn.com.chengmandian.core.web.pojo.AgentInfo;
import cn.com.chengmandian.core.web.pojo.AgentUser;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.core.web.utils.SpringContextUtil;
import cn.com.chengmandian.core.common.utils.UrlUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Slf4j
@Component
public class RequestAuthFilter extends OncePerRequestFilter {
    @Autowired
    private AuthProperties authProperties;

    @Autowired
    private AgentCacheService agentCacheService;

    // 403 权限不足
    private void responseError(HttpServletResponse response, Integer code, String message) {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONUtil.toJsonStr(AjaxResult.error(code, message)));
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    // 默认白名单
    private static final List<String> defaultWhiteList = ListUtil.toList(
            "/*/swagger-resources",
            "/*/swagger-resources/**",
            "/*/swagger-ui/**",
            "/*/v3/api-docs",
            "/*/open/**", // 外部接口
            "/*/inner/**");// 内部接口

    private void printRequest(HttpServletRequest request) {
        if (!SpringContextUtil.isDev()) return;
        log.info("=============================================================");
        log.info("RequestURI:{}", request.getRequestURI());
        if (request.getParameterMap() != null && request.getParameterMap().size() > 0) {
            log.info("ParameterMap:{}", JSONUtil.toJsonStr(request.getParameterMap()));
        }
        log.info("=============================================================");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        printRequest(request);
        if (!authProperties.getEnable()) {
            filterChain.doFilter(request, response);
            return;
        }
        String requestURI = request.getRequestURI();
        for (String path : defaultWhiteList) {
            if (UrlUtil.filterUrls(path, requestURI)) {
                filterChain.doFilter(request, response);
                return;
            }
        }
        // 无需识别代理信息的接口，默认也无需登陆
        if (CollectionUtil.isNotEmpty(authProperties.getNoAgentList())) {
            for (String path : authProperties.getNoAgentList()) {
                if (UrlUtil.filterUrls(path, requestURI)) {
                    filterChain.doFilter(request, response);
                    return;
                }
            }
        }
        String agentId = request.getHeader(CommonConstant.HEADER_AGENT);
        if (StrUtil.isBlank(agentId)) agentId = request.getServerName();
        AgentInfo agentInfo = agentCacheService.getAgent(agentId);
        if (agentInfo == null) {
            log.error("代理标识不存在: {}", agentId);
            responseError(response, ResponseCode.ACCESS_FORBIDDEN, "代理标识不存在");
            return;
        }
        String areaId = request.getHeader(CommonConstant.HEADER_AREA);
        if (StrUtil.isNotEmpty(areaId)) {
            try {
                agentInfo.setAreaId(Integer.parseInt(areaId));
            } catch (Exception e) {
                // 不拦截这个错误，就当没地区
                log.error("区域处理出错: {}，{}",agentId, areaId);
            }
        }
        LocalMapUtil.setAgentInfo(agentInfo);

        // 无需登陆的接口
        if (CollectionUtil.isNotEmpty(authProperties.getWhiteList())) {
            for (String path : authProperties.getWhiteList()) {
                if (UrlUtil.filterUrls(path, requestURI)) {
                    filterChain.doFilter(request, response);
                    return;
                }
            }
        }
        String clientId = request.getHeader(CommonConstant.HEADER_CLIENT_ID);
        if (StrUtil.isBlank(clientId)) clientId = request.getServerName();
        ClientsEnum client = ClientsEnum.codeOf(clientId);
        if (client == null) {
            log.error("代理客户端错误: {}", clientId);
            responseError(response, ResponseCode.ACCESS_DENIED, "代理客户端错误");
            return;
        }

        // 如果应用配置了用户类型
        if (StrUtil.isNotEmpty(authProperties.getUserType())) {
            // 如果配置的用户类型和提交的客户端用户类型不匹配
            if (!ArrayUtil.contains(authProperties.getUserType().split(","), client.getUserType())) {
                log.error("非法用户类型: {}", authProperties.getUserType());
                responseError(response, ResponseCode.ACCESS_DENIED, "非法用户类型");
                return;
            }
        }

        // 获取，并校验
        AgentUser loginUser = AuthUtil.getAgentUser(request, client, agentInfo);
        if (loginUser == null) {
            responseError(response, ResponseCode.TOKEN_INVALID, "登录信息失效：" + request.getRequestURI());
            return;
        }

        // 禁止重复登陆
        if (!client.getRepeatLogin()) {
            if (!AuthUtil.checkTenant(client, loginUser, request, agentInfo.getTokenSecret())) {
                responseError(response, ResponseCode.TOKEN_INVALID, "登陆信息已失效!");
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

}
